package controllers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"runtime/debug"
	"strings"
	"time"
)

type StarveResult struct {
	Result			string
	ErrorLogs		[]string
	FileContent		string
}

type LogFileAttr struct {
	Name			string
	Size			string
	ModifyTime		string
	View			string
}



const RestartInternal int64 = 30 					// 重启指令间隔
const HotUpdateInternal int64 = 20 					// 热更指令间隔
const RestartOrHotUpdateInternal int64 = 20 					// 热更指令间隔

var SizeUnit = []string{"B", "KB", "MB", "GB", "TB"}			// 文件大小单位

var names = []string{"liuqianwei", "liyue", "jianglinfeng", "zhengzixuan", "panglei", "wuhuizhong", "xiaochunhui", "zhangyang", "zhujin", "getianfang", "chengmiao", "huangsiyu", "wangjianan", "lingguihao"}


var ServerTypePathLinux = map[string]string{"common":"/root/ServerDST", "cbtb":"/root/starve-cbtb/ServerDST_CBTB_Release"}
var ServerTypePathWindows = map[string]string{"common":"D:/go", "cbtb":"D:/go"}

var KeyName = make(map[string]string)
var LastRestartTime int64 = 0


func GetServerPath(serverType string) string{

	serverTypePath := ServerTypePathLinux

	if runtime.GOOS == "windows" {
		serverTypePath = ServerTypePathWindows
	}

	path,ok := serverTypePath[serverType]
	if ok {
		return path
	}

	return ""
}

func encodePlayerName(beforeName string) (returnName string){
	afterName := []rune(beforeName)
	for _,ch := range afterName {
		returnName = returnName + fmt.Sprintf("%03d", ch)
	}
	return
}


func execRestart(serverDstPath string, param string) (result []byte, err error){
	err = os.Chdir(serverDstPath)
	if err != nil {
		return
	}
	command :=  serverDstPath + `/restart.sh ` + param
	cmd := exec.Command("/bin/bash", "-c", command)

	result, err = cmd.Output()
	if err != nil {
		return
	}
	return
}

func loadConf() {
	for _,name := range names {
		key := encodePlayerName(name)
		KeyName[key] = name
	}
	logs.Info("names:%v", KeyName)
}


type MainController struct {
	beego.Controller
}

func init() {
	logs.SetLogger("console")
	loadConf()
}


func (c *MainController)errorLog(f string, v ...interface{}) {
	result := f
	if len(v) > 0 {
		result = fmt.Sprintf(f, v)
	}
	starveResult := &StarveResult{Result:result}
	jsonRes,err:= json.Marshal(starveResult)
	if err != nil {
		logs.Error("json.Marshal error:%v", err)
		c.Ctx.WriteString(fmt.Sprintf("json.Marshal error:%v", err))
		return
	}
	logs.Error(string(jsonRes))
	c.Ctx.WriteString(string(jsonRes))
}

func FormatSize(srcSize int64) string {
	maxSizeUnit := len(SizeUnit)
	i := 0
	for ;srcSize >= 1024 && i < (maxSizeUnit - 1); i++ {
		srcSize = srcSize / 1024
	}
	return fmt.Sprintf("%d %s", srcSize, SizeUnit[i])

}

// guarantee error logs are int the front of the returned slice
func GetAllFile(pathname string, logToday bool, logError bool, serverType string) (s []*LogFileAttr, err error) {
	rd, err := ioutil.ReadDir(pathname)
	if err != nil {
		fmt.Println("read dir fail:", err)
		return
	}

	todayStr := time.Now().Format("2006-01-02")

	if s == nil {
		s = []*LogFileAttr{}
	}

	var otherLogs []*LogFileAttr

	for _, fi := range rd {
		if !fi.IsDir() {
			fullName := pathname + "/" + fi.Name()
			info, err := os.Stat(fullName)//Stat获取文件属性
			if err != nil {
				fmt.Println("os.Stat err =",err)
				continue
			}

			indexError := strings.Index(info.Name(), "error")
			if (logToday && strings.Index(info.Name(), todayStr) < 0) || (logError && indexError < 0) {
				continue
			}



			logFileAttr := &LogFileAttr{Name:info.Name(), Size:FormatSize(info.Size()), ModifyTime:info.ModTime().Format("2006-01-02 15:04:05"),
				View:"errorlogcontent?server_type=" + serverType + ";file_name=" + info.Name()}

			if indexError >= 0 {
				s = append(s, logFileAttr)
			} else {
				otherLogs = append(otherLogs, logFileAttr)
			}
		}
	}

	s = append(s, otherLogs...)

	return
}

func FormatNavAddr(serverType string, execFunc string) string{

	serverName := ""
	if serverType == "common" {
		serverName = "内网公共"
	} else if serverType == "cbtb" {
		serverName = "CBTB"
	}

	funcName := ""
	if execFunc == "restart" {
		funcName = "重启"
	} else if execFunc == "hotupdate" {
		funcName = "热更"
	} else if execFunc == "errorlog" {
		funcName = "日志"
	}

	return serverName + " >> " + funcName + " >>"

}

func (c *MainController) Get() {
	c.TplName = "index.html"
}



func (c *MainController) RestartOrHotUpdate() {

	defer func() {
		if err := recover(); err != nil {
			logs.Info("MainController RestartOrHotUpdate panic:%s", debug.Stack())
		}
	}()

	serverType := c.GetString("server_type")
	if len(serverType) <= 0 {
		c.errorLog("MainController RestartOrHotUpdate server_type empty")
		return
	}

	fullPath := GetServerPath(serverType)
	if len(fullPath) <= 0 {
		c.errorLog("MainController RestartOrHotUpdate server_type invalid")
		return
	}

	execFunc := c.GetString("exec_func")
	if len(execFunc) <= 0 {
		c.errorLog("MainController RestartOrHotUpdate exec_func empty")
		return
	}

	isCall := c.GetString("call")
	if len(isCall) <= 0 {		// 打开页面
		c.Data["NavAddr"] = FormatNavAddr(serverType, execFunc)
		c.Data["ServerType"] = serverType
		c.Data["ExecFunc"] = execFunc
		c.TplName = "index.html"
		return
	}

	now := time.Now().Unix()

	if now - LastRestartTime < RestartOrHotUpdateInternal {
		c.errorLog("MainController RestartOrHotUpdate during RestartOrHotUpdateInternal internal ignore")
		return
	}

	LastRestartTime = now

	result,err := execRestart(fullPath, execFunc)
	if err != nil {
		c.errorLog("MainController RestartOrHotUpdate execRestart error:%v", err)
		return
	}
	logs.Info("MainController RestartOrHotUpdate result:%s", result)

	starveResult := &StarveResult{Result:string(result)}
	jsonRes,err:= json.Marshal(starveResult)
	if err != nil {
		c.errorLog("MainController ErrorLogContent. error:%v", err)
		return
	}
	c.Ctx.WriteString(string(jsonRes))

}

func (c *MainController)ErrorLog() {

	defer func() {
		if err := recover(); err != nil {
			logs.Error("MainController ErrorLog panic:%s", debug.Stack())
		}
	}()

	serverType := c.GetString("server_type")
	if len(serverType) <= 0 {
		c.errorLog("MainController ErrorLog server_type empty")
		return
	}

	//fullPath,ok := ServerTypePath[serverType]
	//if !ok {
	//	c.errorLog("MainController ErrorLog server_type invalid")
	//	return
	//}

	//errorLogs,err := GetAllFile(fullPath + "/logs/", true, false, serverType)
	//if err != nil {
	//	logs.Error("MainController ErrorLog GetAllFile err:%v", err)
	//}

	c.Data["NavAddr"] = FormatNavAddr(serverType, "errorlog")
	c.Data["ServerType"] = serverType
	c.Data["ErrorLogs"] = 1
	c.TplName = "index.html"

}

func (c *MainController)GetErrorLogs() {

	defer func() {
		if err := recover(); err != nil {
			logs.Error("MainController GetErrorLogs panic:%s", debug.Stack())
		}
	}()

	serverType := c.GetString("server_type")
	if len(serverType) <= 0 {
		c.errorLog("MainController GetErrorLogs server_type empty")
		return
	}

	fullPath := GetServerPath(serverType)
	if len(fullPath) <= 0 {
		c.errorLog("MainController GetErrorLogs server_type invalid")
		return
	}

	errorLogs,err := GetAllFile(fullPath + "/logs/", true, false, serverType)
	if err != nil {
		logs.Error("MainController GetErrorLogs GetAllFile err:%v", err)
	}

	jsonRes,err:= json.Marshal(errorLogs)
	if err != nil {
		c.errorLog("MainController GetErrorLogs. error:%v", err)
		return
	}
	c.Ctx.WriteString(string(jsonRes))

}

func (c *MainController)ErrorLogContent() {

	defer func() {
		if err := recover(); err != nil {
			logs.Error("MainController ErrorLogContent panic:%s", debug.Stack())
		}
	}()


	serverType := c.GetString("server_type")
	fileName := c.GetString("file_name")

	logs.Info("MainController ErrorLogContent. server_type:%v, file_name:%v", serverType, fileName)

	if len(serverType) <= 0 || len(fileName) <= 0 {
		c.errorLog("MainController ErrorLogContent. server_type or filename empty")
		return
	}

	fullPath := GetServerPath(serverType)
	if len(fullPath) <= 0 {
		c.errorLog("MainController ErrorLogContent server_type invalid")
		return
	}

	fullName := fullPath + "/logs/" + fileName

	fileContent, err := ioutil.ReadFile(fullName) // just pass the file name
	if err != nil {
		c.errorLog("MainController ErrorLogContent. error:%v", err)
		return
	}

	c.Ctx.WriteString(string(fileContent))
	//starveResult := &StarveResult{FileContent:string(b)}
	//jsonRes,err:= json.Marshal(starveResult)
	//if err != nil {
	//	logs.Error("json.Marshal error:%v", err)
	//	c.errorLog("MainController ErrorLogContent. error:%v", err)
	//	return
	//}
	//c.Ctx.WriteString(string(jsonRes))

}

// 跑马灯
func (c *MainController)NotifyClient() {

	defer func() {
		if err := recover(); err != nil {
			logs.Info("MainController NotifyClient panic:%s", debug.Stack())
		}
	}()

	serverType := c.GetString("server_type")
	if len(serverType) <= 0 {
		c.errorLog("MainController NotifyClient server_type empty")
		return
	}

	fullPath := GetServerPath(serverType)
	if len(fullPath) <= 0 {
		c.errorLog("MainController NotifyClient server_type invalid")
		return
	}

	partition := 1
	serverStr := "内网公共"
	if serverType == "cbtb" {
		partition = 11
		serverStr = "CBTB"
	}

	now := time.Now().Unix()

	data := make(map[string]map[string]interface{})

	body := map[string]interface{}{
		"Partition":partition,
		"RevolvingId":now,
		"BeginTime":now - 300,
		"EndTime":now + 120,
		"Priority":1,
		"Type":1,
		"Frequency":10,
		"Versions":"0",
		"IsChat":1,
		"Country_count":2,
		"Country":[]int{156,124},
		"RevolvingLanguageList_count":2,
		"RevolvingLanguageList":[]map[string]string{{"Language": "zh-IE", "Content": serverStr + "will restart"}, {"Language": "en-IE", "Content":serverStr + "will restart"}},
	}
	head := map[string]interface{}{
		"Authenticate":"",
		"Cmdid":4269,
		"PacketLen":0,
		"Result":0,
		"RetErrMsg":"",
		"SendTime":0,
		"Seqid":0,
		"ServiceName":"",
		"Version":0,
	}

	data["body"] = body
	data["head"] = head

	b, err := json.Marshal(data)
	if err != nil {
		c.errorLog("MainController NotifyClient. error:%v", err)
		return
	}

	req, err := http.NewRequest("POST", "http://172.29.11.33:8082/idip", bytes.NewBuffer(b))
	if err != nil {
		c.errorLog("MainController NotifyClient. error:%v", err)
		return
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		c.errorLog("MainController NotifyClient. error:%v", err)
		return
	}
	defer resp.Body.Close()

	rspBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		c.errorLog("MainController NotifyClient. error:%v", err)
		return
	}

	logs.Info("MainController NotifyClient rspBody:%s", rspBody)

	starveResult := &StarveResult{Result:string(rspBody)}
	jsonRes,err:= json.Marshal(starveResult)
	if err != nil {
		c.errorLog("MainController NotifyClient. error:%v", err)
		return
	}
	c.Ctx.WriteString(string(jsonRes))

}
